package com.gimi.cloud.jimi.client.controller;

import com.gimi.cloud.jimi.client.service.UserService;
import com.gimi.cloud.jimi.client.service.userInfo.UserInfoService;
import com.gimi.cloud.jimi.common.enums.ResponseCodeEnum;
import com.gimi.cloud.jimi.common.enums.common.RedisKeyEnum;
import com.gimi.cloud.jimi.common.jwt.Jwt;
import com.gimi.cloud.jimi.common.utils.*;
import com.gimi.cloud.jimi.common.utils.reqhead.Response;
import com.gimi.cloud.jimi.common.utils.reqhead.Util;
import com.gimi.cloud.jimi.commonService.common.CommonCoreNoticeService;
import com.gimi.cloud.jimi.commonService.common.JmConfigService;
import com.gimi.cloud.jimi.dao.dao.core.MessageDao;
import com.gimi.cloud.jimi.dao.dao.core.MessageTemplateDao;
import com.gimi.cloud.jimi.dao.dao.userInfo.UserAppInformationDao;
import com.gimi.cloud.jimi.dao.redis.IRedisService;
import com.gimi.cloud.jimi.dto.user.UserDTO;
import com.gimi.cloud.jimi.dto.userInfo.RdUserInfoDTO;
import com.gimi.cloud.jimi.entity.user.User;
import com.gimi.cloud.jimi.entity.userInfo.UserAppInformation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * Created by shazhiwei on 2017/10/16.
 */
@RestController
@RequestMapping("/register")
public class RegisterController {

    private UserService userService;
    private IRedisService redisService;
    private CommonCoreNoticeService coreNoticeService;
    private UserInfoService userInfoService;
    private MessageTemplateDao messageTemplateDao;
    private MessageDao messageDao;
    private JmConfigService jmConfigService;
    private UserAppInformationDao userAppInformationDao;
    private static Logger logger = LoggerFactory.getLogger(RegisterController.class);

    @Autowired
    private RegisterController(UserService userService, UserInfoService userInfoService, MessageDao messageDao,
                               IRedisService redisService, CommonCoreNoticeService coreNoticeService, MessageTemplateDao messageTemplateDao,
                               JmConfigService jmConfigService,UserAppInformationDao userAppInformationDao) {
        this.userService = userService;
        this.redisService = redisService;
        this.coreNoticeService = coreNoticeService;
        this.userInfoService = userInfoService;
        this.messageTemplateDao = messageTemplateDao;
        this.messageDao = messageDao;
        this.jmConfigService = jmConfigService;
        this.userAppInformationDao = userAppInformationDao;
    }

    /**
     * 验证注册号码是否可用
     *
     * @param phone    电话号码
     * @param response 返回内容
     */
    @RequestMapping("/checkPhoneAvailable")
    public void checkPhoneAvailable(@RequestBody String phone, HttpServletResponse response) {
        String phoneFormat = "^((13[\\d])|(15[0-35-9])|(18[\\d])|(147)|(17[\\d]))\\d{8}$";
        Pattern p = Pattern.compile(phoneFormat);
        Matcher m = p.matcher(phone);
        Response resp = new Response();
        ResponseCodeEnum status = ResponseCodeEnum.INVALID;
        if (m.matches()) {
            User user = userService.checkPhoneAvailable(phone);// 绑定该手机的用户
            if (user == null) {
                status = ResponseCodeEnum.SUCCESSED;
            } else {
                status = ResponseCodeEnum.ERR_PHONE_USED;
            }
        } else {
            status = ResponseCodeEnum.ERR_PHONE_INVALID;
        }
        resp.setResponseCode(status);
        Util.sendResponse(JsonUtil.toJson(resp), response);
    }

    /**
     * 用户登录(密码登录)
     */
    @RequestMapping("/doLoginByPassword")
    public void doLoginByPassword(@RequestBody UserDTO params, HttpServletResponse response) throws Exception {
        Response resp = new Response();
        String id = params.getMobilePhone();
        String pwd = params.getPwd();

        User user = loginValidate(id, pwd, response);

        //这里使用新的jwt替换原来的token校验方式
        String token = getToken(user);
        //将token信息放在redis中
        String key = RedisKeyEnum.JIMI_APP_PRE.getKey() + "user:tokenStore:userId_" + user.getUserId();
        redisService.set(key, token);
        redisService.expire(key, 1000 * 60 * 60 * 24 * 7);
        resp.setResponseCode(ResponseCodeEnum.SUCCESSED, "登录成功");
        resp.setRes_data(token);
        //TODO 因第一版，简单的记录用户登录日志，以后接入getInformation接口后，删除此段代码
        /**------------    简单记录登录日志，开始------------*/
        try {//进行try，catch，以免影响登录业务
            UserAppInformation userAppInformation = new UserAppInformation();
            userAppInformation.setAddTime(new Date());
            userAppInformation.setUserId(user.getUserId());
            userAppInformation.setSaveType(1);//0：验证码登录;1:密码登录
            userAppInformationDao.insertSelective(userAppInformation);
        }catch (Exception e){
            e.printStackTrace();
        }
        /**------------    简单记录登录日志，结束------------*/

        Util.sendResponse(JsonUtil.toJson(resp), response);
    }

    /**
     * 验证码登录
     *
     * @throws IOException
     */
    @RequestMapping("/doLoginByPwdCode")
    public void doLoginByPwdCode(@RequestBody UserDTO userDTO, HttpServletResponse response) throws Exception {
        String identifier = userDTO.getMobilePhone();
        String code = userDTO.getCode();
        Response resp = new Response();
        //校验电话号码格式
        ResponseCodeEnum status = checkPhone(identifier);
        if (status != null) {
            resp.setResponseCode(status);
            Util.sendResponse(JsonUtil.toJson(resp), response);
            return;
        }
        //校验验证码
        String key = RedisKeyEnum.JIMI_APP_PRE.getKey() + "verification_code:loginByPwdcode:" + identifier;
        status = redisCheckCode(code, key);
        if (status != null) {
            logger.info("===>验证码校验未通过");
            resp.setResponseCode(status);
            Util.sendResponse(JsonUtil.toJson(resp), response);
            return;
        }
        logger.info(String.format("用户%s的验证码%s通过了校验", identifier, code));
        //此处增加一个用户的判断，如果用户不存在，则新增，如果存在，则不操作
        User isUser = userService.getUserByMobilePhone(userDTO);
        if (isUser == null) {
            isUser = new User();
            isUser.setMobilePhone(identifier);
            isUser.setUserName(identifier);
            isUser.setAddTime(new Date());
            isUser.setStatus(0);
            int userId = userService.insertReturnUserId(isUser);
            logger.info("===>新增用户" + userId);
            //并且新增一条userInfo
            RdUserInfoDTO userInfoDTO = new RdUserInfoDTO();
            userInfoDTO.setUserId(isUser.getUserId());
            userInfoDTO.setMobilePhone(identifier);
            userInfoDTO.setAddTime(new Date());
            userInfoService.addUserInfo(userInfoDTO);
        }

        //这里使用新的jwt替换原来的token校验方式
        String token = getToken(isUser);
        //将token信息放在redis中
        String tokenkey = RedisKeyEnum.JIMI_APP_PRE.getKey() + "user:tokenStore:userId_" + isUser.getUserId();
        redisService.set(tokenkey, token);
        redisService.expire(tokenkey, 1000 * 60 * 60 * 24 * 7);
        resp.setResponseCode(ResponseCodeEnum.SUCCESSED, "登录成功");
        Map<String, String> data = new HashMap<>();
        data.put("token", token);
        String temp = "0";//没有密码
        if (StringUtil.isNotBlank(isUser.getPwd())) {
            temp = "1";//有密码
        }
        data.put("isHasPassword", temp);
        resp.setRes_data(data);
        //TODO 因第一版，简单的记录用户登录日志，以后接入getInformation接口后，删除此段代码
        /**------------    简单记录登录日志，开始------------*/
        try {//进行try，catch，以免影响登录业务
            UserAppInformation userAppInformation = new UserAppInformation();
            userAppInformation.setAddTime(new Date());
            userAppInformation.setUserId(isUser.getUserId());
            userAppInformation.setSaveType(0);//0：验证码登录;1:密码登录
            userAppInformationDao.insertSelective(userAppInformation);
        }catch (Exception e){
            e.printStackTrace();
        }
        /**------------    简单记录登录日志，结束------------*/
        Util.sendResponse(JsonUtil.toJson(resp), response);
    }

    /**
     * 校验修改密码验证码
     *
     * @throws IOException
     */
    @RequestMapping("/checkModifypassword")
    public void checkModifypassword(@RequestBody UserDTO userDTO, HttpServletResponse response) throws Exception {
        String identifier = userDTO.getMobilePhone();
        String code = userDTO.getCode();
        Response resp = new Response();
        //校验验证码
        //ResponseCodeEnum status = checkCode(AppCommons.RESET_PWD_CODE_MAP, identifier, code);
        String key = RedisKeyEnum.JIMI_APP_PRE.getKey() + "verification_code:modifypasswordCode:" + identifier;
        ResponseCodeEnum status = redisCheckCode(code, key);
        if (status != null) {
            resp.setResponseCode(status);
            Util.sendResponse(JsonUtil.toJson(resp), response);
            return;
        }
        //此处增加一个用户的判断，如果用户不存在，则新增，如果存在，则不操作
        User isUser = userService.getUserByMobilePhone(userDTO);
        if (isUser == null) {
            isUser = new User();
            isUser.setMobilePhone(identifier);
            //isUser.setUserId(UUIDUtil.get32UUID());
            isUser.setAddTime(new Date());
            int userId = userService.insertReturnUserId(isUser);
            //并且新增一条userInfo
            RdUserInfoDTO userInfoDTO = new RdUserInfoDTO();
            userInfoDTO.setUserId(isUser.getUserId());
            userInfoDTO.setMobilePhone(identifier);
            userInfoDTO.setAddTime(new Date());
            userInfoService.addUserInfo(userInfoDTO);
        }

        //这里使用新的jwt替换原来的token校验方式
        String token = getToken(isUser);
        //将token信息放在redis中
        String tokenkey = RedisKeyEnum.JIMI_APP_PRE.getKey() + "user:tokenStore:userId_" + isUser.getUserId();
        redisService.set(tokenkey, token);
        redisService.expire(tokenkey, 1000 * 60 * 60 * 24 * 7);
        resp.setResponseCode(ResponseCodeEnum.SUCCESSED, "登录成功");
        resp.setRes_data(token);
        Util.sendResponse(JsonUtil.toJson(resp), response);
    }

    /**
     * 校验初始化密码的验证码
     *
     * @throws IOException
     */
    @RequestMapping("/doInitPassword")
    public void doInitPassword(@RequestBody UserDTO userDTO, HttpServletResponse response) throws Exception {
        String identifier = userDTO.getMobilePhone();
        String code = userDTO.getCode();
        Response resp = new Response();

        User isUser = userService.getUserByMobilePhone(userDTO);
        //检验user是否存在
        checkUserBean(isUser,response);
        //校验验证码
        String key = RedisKeyEnum.JIMI_APP_PRE.getKey() + "verification_code:initPassword:" + identifier;
        ResponseCodeEnum status = redisCheckCode(code, key);
        if (status != null) {
            resp.setResponseCode(status);
            Util.sendResponse(JsonUtil.toJson(resp), response);
            return;
        }
        //如果验证码校验通过，则可以进行保存密码操作,先将AES加密的密码解密，然后MD5加密
        String pwd = userDTO.getPwd();

        isUser.setPwd(pwd);
        userService.modifyPwd(isUser);

        resp.setResponseCode(ResponseCodeEnum.SUCCESSED, "初始化密码成功");
        Util.sendResponse(JsonUtil.toJson(resp), response);
    }

    /**
     * 初始化密码
     *
     * @throws IOException
     */
    @RequestMapping("/initPassword")
    public void initPassword(@RequestBody UserDTO userDTO, HttpServletResponse response) throws Exception {
        String identifier = userDTO.getMobilePhone();
        Response resp = new Response();
        logger.info(String.format("===>初始化用户%s的密码为%s",identifier,userDTO.getPwd()));
        User isUser = userService.getUserByMobilePhone(userDTO);
        //检验user是否存在
        checkUserBean(isUser,response);
        //如果验证码校验通过，则可以进行保存密码操作,先将AES加密的密码解密，然后MD5加密
        String pwd = userDTO.getPwd();

        isUser.setPwd(pwd);
        userService.modifyPwd(isUser);

        resp.setResponseCode(ResponseCodeEnum.SUCCESSED, "初始化密码成功");
        Util.sendResponse(JsonUtil.toJson(resp), response);
    }

    /**
     * 修改登录密码
     *
     * @return
     * @throws Exception
     */
    @RequestMapping("/modifyPassword")
    public void modifyPwd(@RequestBody UserDTO params, HttpServletResponse response) throws Exception {
        Response resp = new Response();
        logger.info("===>userId = " + params.getUserId());
        User user = getAppSessionUser(params.getUserId() + "", response);
        //检验user是否存在
        checkUserBean(user,response);

        String new_pwd = params.getNewPwd();
        String old_pwd = params.getOldPwd();
        //判断是否有老密码
        if (StringUtil.isBlank(user.getPwd())) {
            resp.setResponseCode(ResponseCodeEnum.ERR_PWD_NOT_CORRECT, "未初始化密码，请直接初始化密码!");
            Util.sendResponse(JsonUtil.toJson(resp), response);
            return;
        }
        //判断旧密码是否和old_pwd一致
        logger.info(String.format("===>用户%s的数据库密码为:%s,提交的原密码为:%s",user.getMobilePhone(),user.getPwd(),old_pwd));

        if (!user.getPwd().equals(old_pwd)) {
            resp.setResponseCode(ResponseCodeEnum.ERR_PWD_NOT_CORRECT, "输入的旧密码不正确");
            Util.sendResponse(JsonUtil.toJson(resp), response);
            return;
        }

        logger.info(String.format("===>用户%s提交的新密码为%s",user.getMobilePhone(),new_pwd));

        if (user.getPwd().equals(new_pwd)) {
            resp.setResponseCode(ResponseCodeEnum.ERR_PWD_REPEAT, "新密码不能与原密码一致");
            Util.sendResponse(JsonUtil.toJson(resp), response);
            return;
        }

        user.setPwd(new_pwd);
        userService.modifyPwd(user);
        resp.setResponseCode(ResponseCodeEnum.SUCCESSED, "修改密码成功");
        Util.sendResponse(JsonUtil.toJson(resp), response);
    }

    /**
     * 修改密码(通过手机验证码修改)
     *
     * @return
     * @throws Exception
     */
    @RequestMapping("/modifyPasswordByPwdcode")
    public void modifyPasswordByPwdcode(@RequestBody UserDTO params, HttpServletResponse response) throws Exception {
        Response resp = new Response();
        User user = getAppSessionUser(params.getUserId() + "", response);
        //检验user是否存在
        checkUserBean(user,response);
        String new_pwd = params.getNewPwd();

        //新密码不能与原密码一致
        logger.info("手机号为===" + user.getMobilePhone() + "===aes解密为" + new_pwd);
        logger.info("手机号为===" + user.getMobilePhone() + "===MD5加密为" + new_pwd);

        user.setPwd(new_pwd);
        userService.modifyPwd(user);
        resp.setResponseCode(ResponseCodeEnum.SUCCESSED, "修改密码成功");
        Util.sendResponse(JsonUtil.toJson(resp), response);

    }

    /**
     * 获取User对象
     *
     * @return
     */
    protected User getAppSessionUser(String userId, HttpServletResponse response) {
        Response resp = new Response();
        if (StringUtil.isEmpty(userId)) {
            resp.setResponseCode(ResponseCodeEnum.ERR_USER_INVALID);
            Util.sendResponse(JsonUtil.toJson(resp), response);
            return null;
        }
        User user = userService.getUserById(Integer.valueOf(userId));
        //检验user是否存在
        checkUserBean(user,response);
        return user;
    }

    /*
	 * 验证手机格式及是否被占用
	 *
	 * @param phone
	 */
    private ResponseCodeEnum checkPhone(String phone) {
        ResponseCodeEnum status = null;
        String phoneFormat = "^((13[\\d])|(15[0-35-9])|(18[\\d])|(147)|(17[\\d]))\\d{8}$";
        Pattern p = Pattern.compile(phoneFormat);
        Matcher m = p.matcher(phone);
        if (m.matches()) {
//            User user = userService.checkPhoneAvailable(phone);// 绑定该手机的用户
//            if (user != null) {
//                status = ResponseCodeEnum.ERR_PHONE_USED;
//            }
        } else {
            status = ResponseCodeEnum.ERR_PHONE_INVALID;
        }
        return status;
    }

    /**
     * redis检验验证码
     *
     * @param code
     */
    protected ResponseCodeEnum redisCheckCode(String code, String redisKey) {
        if (StringUtil.isBlank(code)) {
            return ResponseCodeEnum.ACCESS_ERROR;
        }
        ResponseCodeEnum status = null;
        String redisValue = redisService.get(redisKey);
        if (StringUtil.isBlank(redisValue)) {
            return ResponseCodeEnum.ACCESS_GET_ERROR;//"请先获取验证码"
        }
        try {
            Map<String, String> map = JsonUtil.jsonToMap(redisValue);
            String redisCode = map.get("code");
            if (StringUtil.isBlank(redisCode)) {
                return ResponseCodeEnum.ACCESS_GET_ERROR;
            }
            if (!redisCode.equals(code)) {
                return ResponseCodeEnum.ACCESS_ERROR;
            }
        } catch (Exception e) {
            return ResponseCodeEnum.ACCESS_GET_ERROR;
        }

        return status;
    }

    private User loginValidate(String id, String pwd, HttpServletResponse response) throws UnsupportedEncodingException {
        Response resp = new Response();
        User user = null;
        if (Validates.isPhone(id)) {
            user = userService.checkPhoneAvailable(id);
        }
        if (user == null) {
            resp.setResponseCode(ResponseCodeEnum.ERR_USER_INVALID);
            Util.sendResponse(JsonUtil.toJson(resp), response);
            return null;
        }
        //密码为空，则直接提示请先设置密码
        if(StringUtil.isBlank(user.getPwd())){
            resp.setResponseCode(ResponseCodeEnum.ERR_PWD_NOT_EXIST);
            Util.sendResponse(JsonUtil.toJson(resp), response);
            return null;
        }
        //账号被锁定
        if (user.getStatus()==1) {
            resp.setResponseCode(ResponseCodeEnum.ERR_USER_LOCKED_CAUSE_PWD);
            Util.sendResponse(JsonUtil.toJson(resp), response);
            return null;
        }
        if (!pwd.equalsIgnoreCase(user.getPwd())) {
            loginFailProtect(user, response);
            return null;
        }
        return user;

    }

    /*
	 * 登录失败处理
	 * @param user
	 */
    private void loginFailProtect(User user, HttpServletResponse response) {
        Response resp = new Response();
        long now = System.currentTimeMillis();
        long timeRange = 300 * 1000;// 时间间隔5分钟
        int allowedFailTimes = 5;// 允许的失败次数
        String tmpLastFailTime = redisService.get(RedisKeyEnum.APP_LOGIN_FAIL_LAST_TIME_PRE.getKey() + user.getMobilePhone());
        String failTotalTimes = redisService.get(RedisKeyEnum.APP_LOGIN_FAIL_TOTAL_TIMES.getKey() + user.getMobilePhone());
        Long lastFailTime = null;
        Integer totalTimes = null;
        if (!StringUtil.isBlank(tmpLastFailTime)) {//最新一次失败的时间
            lastFailTime = Long.parseLong(tmpLastFailTime);
        }
        if(!StringUtil.isBlank(failTotalTimes)){//总的失败的次数
            totalTimes = Integer.parseInt(failTotalTimes);
        }
        if (lastFailTime == null) {//最新一次失败的时间
            lastFailTime = (long) 0;
            totalTimes = 0;//失败次数一同刷新成0
        }
        //上次密码错误时间不到两分钟
        if (now - lastFailTime < timeRange) {
            totalTimes++;
            if (totalTimes >= allowedFailTimes) {
                user.setStatus(1);
                userService.update(user);

                //5次登录失败 冻结提醒
                resp.setResponseCode(ResponseCodeEnum.ERR_USER_LOCKED_CAUSE_PWD,"您的账户已冻结");
                Util.sendResponse(JsonUtil.toJson(resp), response);
                return;
            } else {
                //redis里次数加一
                redisService.set(RedisKeyEnum.APP_LOGIN_FAIL_TOTAL_TIMES.getKey() + user.getMobilePhone(), totalTimes+"");
                redisService.expire(RedisKeyEnum.APP_LOGIN_FAIL_TOTAL_TIMES.getKey() + user.getMobilePhone(), 60 * 10);

                resp.setResponseCode(ResponseCodeEnum.ERR_PWD_NOT_CORRECT,"密码错误，您还有" + (allowedFailTimes - totalTimes) + "次输入机会");
                Util.sendResponse(JsonUtil.toJson(resp), response);
                return;
            }
        } else if (now - lastFailTime > timeRange) {
            //上次密码错误时间超过两分钟（包含第一次密码错误）
            redisService.set(RedisKeyEnum.APP_LOGIN_FAIL_LAST_TIME_PRE.getKey() + user.getMobilePhone(), now + "");
            redisService.expire(RedisKeyEnum.APP_LOGIN_FAIL_LAST_TIME_PRE.getKey() + user.getMobilePhone(), 60 * 10);

            redisService.set(RedisKeyEnum.APP_LOGIN_FAIL_TOTAL_TIMES.getKey() + user.getMobilePhone(), "1");
            redisService.expire(RedisKeyEnum.APP_LOGIN_FAIL_TOTAL_TIMES.getKey() + user.getMobilePhone(), 60 * 10);

            //AppCommons.LOGIN_FAIL_LAST_TIME_MAP.put(user.getPhone(), now);
            resp.setResponseCode(ResponseCodeEnum.ERR_PWD_NOT_CORRECT, "用户名或密码不正确");
            Util.sendResponse(JsonUtil.toJson(resp), response);
            return;
        }
    }
    public void checkUserBean(User user,HttpServletResponse response){
        Response resp = new Response();
        if (user == null) {
            resp.setResponseCode(ResponseCodeEnum.ERR_USER_INVALID);
            resp.setRes_msg("用户不存在");
            Util.sendResponse(JsonUtil.toJson(resp), response);
            return;
        }
    }

    /*
	 * 根据用户获取token
	 * @param user
	 * @return
	 * @throws Exception
	 */
    private String getToken(User user) throws Exception {
        String token = "";
        Map<String, Object> payload = new HashMap<String, Object>();
        Date date = new Date();
        payload.put("userId", user.getUserId());//用户id
        payload.put("phone", user.getMobilePhone());//用户手机号
        payload.put("ext", date.getTime() + 1000 * 60 * 60 * 24 * 7);//过期时间24*7小时
        String secret = jmConfigService.getValueByKey(RedisKeyEnum.APP_JWT_TOKEN_SECRET.getKey());
        token = Jwt.createToken(payload, secret);
        logger.info("====token====:" + token);
        return token;
    }

    /*public static void main(String[] args) {
        String phoneFormat = "^((13[\\d])|(15[0-35-9])|(18[\\d])|(147)|(17[\\d]))\\d{8}$";
        //String phoneFormat = "^((1[3,5,8][0-9])|(14[5,7])|(17[0,6,7,8])|(19[7]))\\d{8}$";
        Pattern p = Pattern.compile(phoneFormat);
        Matcher m = p.matcher("18880443285");
        System.out.println(m.matches());
    }*/

}
